<template>
    <div id="app">
        <div class="msg-box">
            <!-- 消息输入框 -->
            <div class="msg-input" ref="msgInput" contenteditable @blur="getAfterBlurIndex" @input="onInput"
                @keydown.delete="handleDelete"></div>
            <!-- 表情列表 -->
            <div class="emoji-list">
                <p>表情列表</p>
                <img v-for="(emoji, key) in emojiList" :key="key" :src="emoji" @click="selectEmoji(key)" />
            </div>
            <button @click="handleSend">发送</button>
        </div>
        <div v-if="showUserList" class="at-user-popup" :style="atPopupStyle">
            <ul>
                <li v-for="user in filteredUsers" :key="user.id" @click="insertAtUser(user)">
                    {{ user.name }}
                </li>
            </ul>
        </div>
        <!-- 消息回显框 -->
        <div class="msg-preview">
            <div class="msg-html" v-html="chatMsgRecord.html"></div>
            <div class="msg-at-users" v-if="chatMsgRecord.atUsers.length">
                <p>被 @ 的用户：</p>
                <ul>
                    <li v-for="user in chatMsgRecord.atUsers" :key="user.id">
                        {{ user.name }}（ID: {{ user.id }}）
                    </li>
                </ul>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, reactive } from "vue";
import Ikun2 from "@/assets/Ikun2.jpg";
import Ikun3 from "@/assets/Ikun3.jpg";
import Ikun4 from "@/assets/Ikun4.jpg";

let msgInput = ref(); // 输入框ref绑定
let chatMsgRecord = ref({
    html: "",
    atUsers: []
});
let focusNode = reactive({}); // 存储光标聚焦节点
let focusOffset = ref(0); // 存储光标聚焦偏移量
let chatInputOffset = reactive({}); // 存储光标聚焦的元素

// 检测 @ 输入，记录光标位置并展示候选用户列表
const showUserList = ref(false);
const atTriggerRange = ref(null); // 用于存储输入 @ 时的 Range
const userList = ref([
    { id: 1, name: "User1" },
    { id: 2, name: "User2" },
    { id: 3, name: "User3" },
    { id: 4, name: "User4" },
    { id: 5, name: "User5" },
    { id: 6, name: "User6" },
    { id: 7, name: "User7" },
    { id: 8, name: "User8" },
]);
const filteredUsers = ref([]); // 当前过滤后的用户
const atKeyword = ref(""); // @ 后面的关键字
const atPopupStyle = ref({ top: "0px", left: "0px" });

const emojiList = {
    "[vueLogo2]": Ikun2,
    "[vueLogo3]": Ikun3,
    "[vueLogo4]": Ikun4,
};

const onInput = (e) => {
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0) return;

    const range = sel.getRangeAt(0).cloneRange();
    const node = range.startContainer; // 光标所在的节点
    const offset = range.startOffset; // 光标所在的偏移量

    if (!node || node.nodeType !== Node.TEXT_NODE) return;

    // 取光标前所有文本，并使用正则匹配是否有以 @xxx 结尾的内容
    const textBeforeCursor = node.textContent.slice(0, offset);
    const match = textBeforeCursor.match(/@([\u4e00-\u9fa5_a-zA-Z0-9]*)$/);

    if (match) {
        const keyword = match[1];
        atKeyword.value = keyword;

        filteredUsers.value = userList.value.filter(user =>
            user.name.includes(keyword)
        );

        if (filteredUsers.value.length > 0) {
            showUserList.value = true;

            // 计算弹窗位置
            const atRange = document.createRange();
            atRange.setStart(node, offset - keyword.length - 1);
            atRange.setEnd(node, offset);
            atTriggerRange.value = atRange;

            const rect = atRange.getBoundingClientRect();
            atPopupStyle.value = {
                top: rect.bottom + window.scrollY + "px",
                left: rect.left + window.scrollX + "px",
            };
        } else {
            // 没有匹配结果，不展示
            showUserList.value = false;
            atTriggerRange.value = null;
        }
    } else {
        showUserList.value = false;
        atKeyword.value = "";
        filteredUsers.value = [];
    }
};

// 点击某个用户，插入 @用户名，并恢复光标
const insertAtUser = (user) => {
    showUserList.value = false;
    if (!atTriggerRange.value) return;

    const mentionEl = document.createElement("span");
    mentionEl.textContent = `@${user.name}`;
    mentionEl.setAttribute("data-user-id", user.id);
    mentionEl.style.color = "#409EFF";
    mentionEl.style.fontWeight = "bold";
    mentionEl.setAttribute("contenteditable", "false");
    mentionEl.classList.add("mention-user");

    const range = atTriggerRange.value;
    range.deleteContents();

    range.insertNode(mentionEl);

    const spaceText = document.createTextNode("\u200B");
    mentionEl.after(spaceText);

    const newRange = document.createRange();
    newRange.setStart(spaceText, 1);
    newRange.setEnd(spaceText, 1);

    const sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(newRange);

    // 清空触发 Range
    atTriggerRange.value = null;
};

const handleDelete = (e) => {
    const sel = window.getSelection();
    if (!sel.rangeCount) return;
    const range = sel.getRangeAt(0);
    const container = range.startContainer;
    const offset = range.startOffset;

    if (container.nodeType === Node.TEXT_NODE && offset > 0) {
        const charToDelete = container.textContent.charAt(offset - 1);
        if (charToDelete === "@") {
            showUserList.value = false;
            atTriggerRange.value = null;
            return;
        }
    }
    let targetNode = null;
    if (container.nodeType === Node.TEXT_NODE) {
        targetNode = offset === 0
            ? container.previousSibling
            : container;
    } else if (container.nodeType === Node.ELEMENT_NODE && offset > 0) {
        targetNode = container.childNodes[offset - 1];
    }

    if (!targetNode) return;

    // 删除整个 @用户 节点
    if (targetNode.nodeType === Node.ELEMENT_NODE && targetNode.classList.contains('mention-user')) {
        e.preventDefault();
        const next = targetNode.nextSibling;
        targetNode.remove();
        // 同步把后面可能的零宽字符也删掉
        if (next && next.nodeType === Node.TEXT_NODE && next.textContent === "\u200B") {
            next.remove();
        }
        // 删除完 span 也可能把最后一个 @ 去掉，隐藏弹框
        const text = msgInput.value.innerText || msgInput.value.textContent;
        if (!/@/.test(text)) {
            showUserList.value = false;
            atTriggerRange.value = null;
        }
        return;
    }

    // 删除紧跟在 mention-user 后的零宽字符时，同样处理
    if (targetNode.nodeType === Node.TEXT_NODE && targetNode.textContent === "\u200B") {
        const prev = targetNode.previousSibling;
        if (prev && prev.nodeType === Node.ELEMENT_NODE && prev.classList.contains('mention-user')) {
            e.preventDefault();
            prev.remove();
            targetNode.remove();
            const text = msgInput.value.innerText || msgInput.value.textContent;
            if (!/@/.test(text)) {
                showUserList.value = false;
                atTriggerRange.value = null;
            }
        }
    }
};

// 聊天输入框失焦时获取失焦前的光标位置
const getAfterBlurIndex = () => {
    console.log('失焦');
    if (window.getSelection) { // 检查浏览器是否支持Selection API
        let sel = window.getSelection(); // 获取当前选区对象
        if (sel.getRangeAt && sel.rangeCount) {
            focusNode = sel.focusNode; // 光标所在的DOM节点
            focusOffset.value = sel.focusOffset; // 光标在节点中的偏移量
            chatInputOffset = sel.getRangeAt(0).cloneRange(); // 获取光标位置，完整的Range对象
        }
    }
}

// 获取输入框选取
const getInputSelection = () => {
    let sel = window.getSelection();
    // 插入内容之前输入框是否已经聚焦获得选区
    if (
        !chatInputOffset.endContainer ||
        (chatInputOffset.endContainer.className != "msg-input" &&
            chatInputOffset.endContainer.parentNode.className != "msg-input" &&
            chatInputOffset.endContainer.parentNode.parentNode.className != "msg-input")
    ) {
        chatInputOffset = document.createRange();
        //用于设置 Range，使其包含一个 Node的内容。
        chatInputOffset.selectNodeContents(document.querySelector(".msg-input"));
        //将包含着的这段内容的光标设置到最后去，true 折叠到 Range 的 start 节点，false 折叠到 end 节点。如果省略，则默认为 false .
        chatInputOffset.collapse(false);
        sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(chatInputOffset);
    }
}
// 点击选择表情
const selectEmoji = (emoji) => {
    getInputSelection(); // 先获得一下输入框焦点
    // 创建图片元素，回显到输入框内
    const img = `<img src="${emojiList[emoji]}" alt='${emoji}' class="emoji" style="width: 36px;height: 36px;object-fit: contain" >`;
    chatInputOffset.collapse(false); //光标移至最后
    // 创建节点并插入
    const node = chatInputOffset.createContextualFragment(img);
    let c = node.lastChild;
    chatInputOffset.insertNode(node);
    if (c) {
        chatInputOffset.setEndAfter(c);
        chatInputOffset.setStartAfter(c);
    }
    let j = window.getSelection();
    j.removeAllRanges();
    j.addRange(chatInputOffset);
}

const handleSend = () => {
    const inputEl = msgInput.value;
    if (!inputEl) return;

    // 获取输入框 HTML 内容
    const html = inputEl.innerHTML;

    // 提取所有 @用户 span
    const mentionEls = inputEl.querySelectorAll(".mention-user");

    const atUsers = Array.from(mentionEls).map(el => ({
        id: el.getAttribute("data-user-id"),
        name: el.textContent.replace(/^@/, '').trim()
    }));

    // 设置聊天消息记录
    chatMsgRecord.value = {
        html,
        atUsers
    };

    // 清空输入框
    inputEl.innerHTML = "";
};

</script>

<style scoped lang="scss">
#app {
    padding: 20px;
}

.msg-box {
    display: flex;
}

.at-user-popup {
    position: absolute;
    background: white;
    border: 1px solid #ddd;
    z-index: 10;
    padding: 6px;
    width: 200px;
    max-height: 100px;
    overflow-y: auto;
    box-sizing: border-box;
    scrollbar-width: thin;
    scrollbar-color: #409EFF #f0f0f0;
}

.at-user-popup::-webkit-scrollbar {
    width: 8px;
}

.at-user-popup::-webkit-scrollbar-thumb {
    background-color: #409EFF;
    border-radius: 10px;
    border: 2px solid #f0f0f0;
}

.at-user-popup::-webkit-scrollbar-track {
    background-color: #f0f0f0;
    border-radius: 10px;
}
.msg-preview {
    margin-top: 20px;
    padding: 10px;
    border: 1px dashed #999;
    border-radius: 8px;
}

.msg-html {
    padding: 6px 0;
}

.msg-at-users {
    font-size: 14px;
    color: #555;
}

.msg-at-users ul {
    padding-left: 20px;
    list-style-type: circle;
}

.emoji-list {
    width: 200px;
    border: 1px solid #ffa117;
    border-radius: 6px;
    padding: 5px;

    img {
        width: 30px;
        cursor: pointer;
        margin-right: 4px;
        border-radius: 5px;
    }
}

.msg-input {
    width: 300px;
    height: 70px;
    border-radius: 6px;
    border: 1px solid #a1a1a1;
    outline: none;
    overflow: hidden;

    &:focus {
        transition: all 0.5s ease-in-out;
        border-color: rgb(12, 165, 165);
    }
}
</style>